﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using TransportLayer;
using System.ComponentModel;


namespace VirtualOperator
{
    public class VirtualNetworkOperator
    {

        private RoutingController rc;

        private string networkName;
        private Communicator communicator;
        public Communicator Communicator { get { return communicator; } }

        //accesible nodes
        private Dictionary<int, VirtualNetwork.Matrix> accessibleMatrixes = new Dictionary<int, VirtualNetwork.Matrix>();
        public Dictionary<int, VirtualNetwork.Matrix> AccessibleMatrixes { get { return accessibleMatrixes; } }

        private Dictionary<int, VirtualNetwork> virtualNetworks = new Dictionary<int, VirtualNetwork>();
        public Dictionary<int, VirtualNetwork> VirtualNetworks { get { return virtualNetworks; } }
        public int Current { get; set; }


        public BindingList<VirtualNetwork> BindingVirtualNetworks = new BindingList<VirtualNetwork>();
        public BindingList<VirtualNetwork.Matrix> BindingAccessibleMatrixes = new BindingList<VirtualNetwork.Matrix>();
        //current
        private VirtualNetwork currentVirtualNetwork;
        public VirtualNetwork CurrentVirtualNetwork
        { get { return currentVirtualNetwork; } set { currentVirtualNetwork = value; } }
        private LogMessages log;
        public LogMessages Log { get { return log; } }

        public event AddAccessibleMatrix AddAccessibleMatrixHandler = null;
        public VirtualNetworkOperator()
        {
            log = LogMessages.GetInstance();
        }

        public string VNOName { get; private set; }

        public void Init()
        {
            VOConfig config = VOConfig.fopen("VNOconfig.xml");
            VNOName = config.Name;
            log.AppendLog(Task.VNO_UP, "Hi!");
            //TODOcommunicator = new Communicator(
            communicator = new Communicator(config);
            rc = new RoutingController(); ;
        }
    

       private int GetTopologyFreeID()
        {
            int index = 1;
            while (virtualNetworks.ContainsKey(index))
            {
                index++;
            }

            return index;
        }
       public int AddVirtualNetwork(string name, int delay, int usage, int errorRate)
       {
           if (!communicator.Connected)
               throw new VNONotConnected();

           int vnNumber = communicator.CreateNewVirtualNetwork(delay,usage,errorRate); //todo
           if (vnNumber > 0 && !virtualNetworks.ContainsKey(vnNumber))
           {
               virtualNetworks.Add(vnNumber, new VirtualNetwork(communicator,rc, vnNumber, name, delay, usage, errorRate, VNOName));
               BindingVirtualNetworks.Add(virtualNetworks[vnNumber]);
           }
           Current = vnNumber;
           if (Current > 0)
           {
               currentVirtualNetwork = virtualNetworks[Current];
               log.AppendLog(Task.NEW_VIRTUAL_NETWORK, CurrentVirtualNetwork.Name, "OK " + name);
           }
           else 
           {
               log.AppendLog(Task.NEW_VIRTUAL_NETWORK, name, "FAIL " + name);
           }
           return vnNumber;
       }
       public VirtualNetwork.Link AddVirtualLink( VirtualNetwork.Matrix src, VirtualNetwork.Matrix trg, int maxDelay, float linkErrorRate, int capacity)
       {
           if (!communicator.Connected)
               throw new VNONotConnected();

           if (currentVirtualNetwork == null)
               throw new NullReferenceException();

  
               bool result  = CurrentVirtualNetwork.AddLink(src, trg ,capacity, maxDelay, linkErrorRate);

               if (result)
               {
                    log.AppendLog(Task.NEW_VIRTUAL_LINK, CurrentVirtualNetwork.Name, "OK " + src.Name + "->"+ trg.Name + " ["  +capacity +"]");
                    return currentVirtualNetwork.Links.Last();
           }
           else
           {
               log.AppendLog(Task.NEW_VIRTUAL_LINK, CurrentVirtualNetwork.Name, "FAIL " + src.Name + "->" +trg.Name );    
               return null; 
           }
       }
      public bool RemoveVirtualNetwork(int id)
      {
              if (!communicator.Connected)
              throw new VNONotConnected();

          log.AppendLog(Task.REMOVE_VIRTUAL_NETWORK, virtualNetworks[id].Name, "OK");
          bool result = virtualNetworks.Remove(id);
          if (result)
          {
              communicator.RemoveVirtualNetwork(id);
              log.AppendLog(Task.REMOVE_VIRTUAL_NETWORK, virtualNetworks[id].Name, "OK");
          }
          else
              log.AppendLog(Task.REMOVE_VIRTUAL_NETWORK, virtualNetworks[id].Name, "FAIL");

          return result;
      }

      public bool AddSink(VirtualNetwork.Matrix nodeSource)
      {
          NwTTP targetNwTTP = null;
          if ((targetNwTTP = communicator.AddSink(nodeSource.ManagerID, currentVirtualNetwork.ID)) != null)
          {
              currentVirtualNetwork.AddNwTTP(targetNwTTP, nodeSource.ID);
              log.AppendLog(Task.ADD_SINK, CurrentVirtualNetwork.Name, "OK " + targetNwTTP.Name);
              return true;
          }
          else
          {
              log.AppendLog(Task.ADD_SINK, CurrentVirtualNetwork.Name, "FAIL ");
              return false;
          }
      }

      public bool AddSource(VirtualNetwork.Matrix nodeTarget)
      {
          NwTTP sourceNwTTP = null;
          if ((sourceNwTTP = communicator.AddSource(nodeTarget.ManagerID, currentVirtualNetwork.ID)) != null)
          {
              currentVirtualNetwork.AddNwTTP(sourceNwTTP, nodeTarget.ID);
              log.AppendLog(Task.ADD_SOURCE, "OK " + sourceNwTTP.Name);
              return true;
          }
          else
          {
              log.AppendLog(Task.ADD_SOURCE, "FAIL ");
              return false; 
          }
      }

      public void GetMatrixes()
      {
          if (!communicator.Connected)
              throw new VNONotConnected();
          accessibleMatrixes = communicator.GetMatrixes();

          foreach (VirtualNetwork.Matrix matrix in accessibleMatrixes.Values.ToList())
          {
              BindingAccessibleMatrixes.Add(matrix);
          }

          log.AppendLog(Task.GET_ACCESIBLE_MATRIXES, "OK");
      }

      public bool ConnectToPicsNC()
      {
          bool result = communicator.Init();
          if (result)
          { 
              log.AppendLog(Task.CONNECT_TO_PICSNC, "OK");
              networkName = communicator.NetworkName;
          }
          else
              log.AppendLog(Task.CONNECT_TO_PICSNC, "FAIL");
          return result;
      }

      public void LoadVirtualNetwork(string filename)
      {
          NetworkConfig nc = NetworkConfig.Open(filename);

          if (!communicator.Connected)
              throw new VNONotConnected();

          if (nc.NetworkName != networkName)
              throw new WrongNetworkTopology();


              if(accessibleMatrixes.Count == 0)
              this.GetMatrixes();
              //tdodo when last argument folat evrywhere delete cast
              this.AddVirtualNetwork(nc.VirtualNetworkName, nc.DelayWeight, nc.UsageWeight, nc.ErrorRateWeight);
              foreach (LinkConfig lc in nc.VirtualLinkList)
              {
                  this.AddVirtualLink(accessibleMatrixes[lc.SourceManagerID], accessibleMatrixes[lc.TargetManagerID], lc.LinkDelay, (int)lc.LinkErrorRate, lc.Capacity);
              }


      }

      public void SaveVirtualNetwork()
      {
          if (!communicator.Connected)
              throw new VNONotConnected();

          if (currentVirtualNetwork == null)
              throw new NullReferenceException();

          NetworkConfig nc = new NetworkConfig();
          nc.NetworkName = networkName;
          nc.VirtualNetworkName = currentVirtualNetwork.Name;
          nc.DelayWeight = currentVirtualNetwork.DelayWeight;
          nc.ErrorRateWeight = currentVirtualNetwork.ErrorRateWeight;
          nc.UsageWeight = currentVirtualNetwork.UsageWeight;
          
          LinkConfig lc;
          foreach (VirtualNetwork.Link link in currentVirtualNetwork.Edges)
          {
              lc = new LinkConfig(link.Source.ManagerID, link.Target.ManagerID, link.Source.Delay, link.Delay, link.ErrorRate, link.MaxCapacity);
              nc.VirtualLinkList.Add(lc);
          }

          nc.Save(nc.VirtualNetworkName+".xml");
      }

      public void Clean()
      {
          communicator.ByeVNO();
          log.AppendLog(Task.VNO_DOWN, "Bye");
          log.Clean();
          communicator.Clean();
      }
    }
}
